import os
import sys
import signal

from ivic.core import Config
from ivic.core import Logging
from ivic.core import Utils
from ivic.core import project_path
from ivic.core.HTTPServer import MixedHTTPServer

class DummyServer:
    def __init__(self, *args):
        pass

daemon_plugins = {
    'vmc': "ivic.vmc.Plugin",
    'vmcweb': "ivic.vmcweb.Plugin",
    'vswitch': "ivic.vswitch.Plugin",
    # vstore doesn't have an SOAP interface, but it needs http GET to serve rootfs
    #'vstore': DummyServer,
    'vsched': "ivic.vsched.Plugin",
    'vspace': "ivic.vspace.Plugin",
    'vprocess': "ivic.vprocess.Plugin",
    'webfarm':"ivic.webfarm.Plugin"
}

class ExitSignal(BaseException):
    pass

def make_signal_handler(logger, config):

    def __signal_handler(signum, frame):
        logger.info('caught signal: %d, daemon exiting...', signum)
        raise ExitSignal, 'signal: %d' % signum

    return __signal_handler

def do_server(logger, config):
    # probe external address
    if not config['external_address']:
        logger.info('no external address defined in daemon config file')
        addr = Utils.external_address()
        logger.info('probed external address: %s', addr)
        config['external_address'] = addr

    if not config['listen_address']:
        listen_address = ''
    endpoint = (listen_address, config['listen_port'])
    logger.info('daemon listen address and port: %s' % repr(endpoint))

    logger.info('daemon initializing...')
    server = MixedHTTPServer(
            endpoint = endpoint,
            www_dir = config['www_dir'],
    )

    # validate capabilities
    caps = config['capabilities']
    logger.debug('capabilities: %s' % repr(caps))
    if type(caps) != list:
        logger.error('bad capabilities. reset it to empty')
        caps = []

    # init capabilities
    enabled_caps = []
    for cap in caps:
        if not daemon_plugins.has_key(cap):
            logger.error('wrong capabilities: %s' % cap)
            continue
        try:
            logger.debug('init capability: %s' % cap)
            plugin = daemon_plugins[cap]
            if type(plugin) == str:
                exec('import ' + plugin)
                daemon_plugins[cap] = eval(plugin)
                logger.info('load capability from file: %s' % eval(plugin).__file__)
            daemon_plugins[cap].init(config)
        except Exception, e:
            logger.exception(e)
            logger.error('error init capability: %s (%s)', cap, str(e))
            continue

        for path, mod in daemon_plugins[cap].soap_mods().items():
            server.register_soap_module(mod, path)
        for path, mod in daemon_plugins[cap].wsgi_mods().items():
            server.register_wsgi_module(mod, path)

        enabled_caps.append(cap)
        logger.info('enabled capability: %s' % cap)

    logger.info('daemon initialized')
    server.serve_loop()
    logger.info('daemon cleaning...')

    # fini capabilities
    for cap in enabled_caps:
        try:
            logger.debug('fini capability: %s' % cap)
            daemon_plugins[cap].fini()
        except Exception, e:
            logger.exception(e)
            logger.error('error fini capability: %s (%s)', cap, str(e))

    logger.info('daemon exit')

def main():
    # daemonized itself at very begining
    if Utils.daemonize():
        # parent process exit
        exit(0)

    # now run in daemon mode

    # load config and initialize logger
    config_file = project_path('/etc/ivic/daemon.yaml')
    try:
        config = Config.load(config_file)
        logger = Logging.get_logger('ivic.daemon.CmdMain', config['log_file'], default = True)
    except Exception, e:
        # steal logger of Config since we don't have one yet
        Config.logger.exception(e)
        Config.logger.fatal('Error in config %s:\n    %s' % (config_file, e))
        exit(1)

    for k, v in config.items():
        if type(v) == str:
            logger.debug('config var: %s = %s', k, v)

    # chdir to run_dir if enabled in config
    try:
        if config['change_dir']:
            os.chdir(config['run_dir'])
    except Exception, e:
        logger.exception(e)
        logger.fatal('cannot chdir to run_dir. daemon exit')
        exit(1)

    # test if pid written in pid_file exists
    try:
        logger.debug('check pid file: %s', config['pid_file'])
        Utils.check_pid_file(config['pid_file'])
    except Utils.PidExistsError, e:
        logger.fatal('failed pid file check: %s. daemon exit', str(e))
        exit(1)

    # write pid to pid_file
    try:
        pid = os.getpid()
        logger.info('daemon pid: %d', pid)
        logger.info('daemon pid file: %s', config['pid_file'])
        pid_fd = open(config['pid_file'], 'w')
        print >>pid_fd, pid
        pid_fd.close()
    except Exception, e:
        logger.exception(e)
        logger.fatal('cannot write pid file. daemon exit')
        exit(1)

    # handle signal
    for s in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP]:
        signal.signal(s, signal.SIG_IGN)
        signal.signal(s, make_signal_handler(logger, config))

    # run server code
    try:
      do_server(logger, config)
    except Exception, e:
        logger.exception(e)
        logger.fatal('internal error. daemon exit')
